#!/usr/bin/env python
""" AnyBackup is a windows specific backup program to keep multiple volumes in sync with multiple volumes
    Copyright (C) 2011  Andrew Hopkinson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Exported Classes:

    drive -- a core AnyBackup object, contains all meta info about a volume plus a link to the root file
    """

import DriveUtil
import re
import os
import socket
import sys
import cPickle
from file import file

class drive:
    """ a core AnyBackup object, contains all meta info about a volume plus a link to the root file """
    BACKUP=2
    CONTENT=3
    ALL=4
    MISSING=9
    port = 60000
    buffer_size = 1024
    NOTEXIST = 'notexist'

    def write(self,_socket, data):
        f = _socket.makefile('wb', self.buffer_size )
        cPickle.dump(data, f, cPickle.HIGHEST_PROTOCOL)
        f.close()
    
    def read(self,_socket):
        f = _socket.makefile('rb', self.buffer_size )
        data = cPickle.load(f)
        f.close()
        return data    
    
    def __init__(self, name, serial, totalSpace, freeSpace, isBackup):
        validExt = []
        skipList = []
        rootFile = None
        self._name = name
        self._serial = serial
        self._totalSpace = totalSpace
        self._freeSpace = freeSpace
        self._isBackup = isBackup
        self._validExt = validExt
        self._skipList = skipList
        self._rootFile = rootFile
        self._pendingWrite = 0
        self._pendingWriteDirs = []
        self._locked = False
        self._remoteHost = None
        self._remotePath = None
        
    def remoteReady(self):
        try:
            if self._remoteHost and self._remotePath:
                return True
            return False
        except AttributeError:
            return False
        
    def setRemoteHost(self,host):
        self._remoteHost = host
        
    def setRemotePath(self,path):
        self._remotePath = path

    def isLocked(self):
        try:
            return self._locked
        except AttributeError:
            return False
    
    def setLocked(self,lockFlag):
        self._locked = lockFlag
        
    def getPendingWrite(self):
        return self._pendingWrite

    def addPendingWrite(self,fileRef):
        if self._pendingWrite+fileRef.getSize() > self._freeSpace*1048576:
            return False
        else:
            self._pendingWriteDirs.append(fileRef.getDir())
            self._pendingWrite += fileRef.getSize()
            return True

    def addPendingDelete(self,fileRef):
        self._pendingWrite -= fileRef.getSize()

    def clearPendingWrite(self):
        self._pendingWriteDirs = []
        self._pendingWrite = 0

    def isBackup(self):
        return self._isBackup

    def setIsBackup(self,isBackup):
        self._isBackup = isBackup

    def getSkipList(self):
        return self._skipList

    def addSkip(self, skip):
        self._skipList.append(skip)

    def setSkipList(self, skipList):
        if skipList:
            self._skipList = skipList

    def getValidExt(self):
        return self._validExt

    def addValidExt(self,ext):
        self._validExt.append(ext)

    def setValidExt(self,validExt):
        if validExt:
            self._validExt = validExt

    def getName(self):
        return self._name

    def setName(self, name):
        self._name = name

    def getSerial(self):
        return self._serial

    def setSerial(self, serial):
        self._serial = serial

    def getTotalSpace(self):
        return self._totalSpace

    def setTotalSpace(self, totalSpace):
        self._totalSpace = totalSpace

    def getFreeSpace(self):
        return self._freeSpace

    def setFreeSpace(self, freeSpace):
        self._freeSpace = freeSpace

    def getRootFile(self):
        return self._rootFile

    def setRootFile(self, rootFile):
        self._rootFile = rootFile

    def addFile(self, file):
        if file:
            self.getRootFile().addFile(file)
            self.updateFreeSpace()
            return True
        return False

    def search(self, regex):
        if self.getRootFile():
            return self.getRootFile().search(regex)
        return None
    
    def remoteIndex(self):
        if not self._remoteHost or not self._remotePath:
            return False
        try:
            s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            s.connect((self._remoteHost,self.port))
            commDict = {}
            commDict['sep'] = '\\'
            commDict['path'] = self._remotePath
            commDict['skip'] = self._skipList
            commDict['validext'] = self._validExt
            commDict['name'] = self._name
            commDict['serial'] = self._serial
            self.write(s,commDict)
            files = self.read(s)
            s.close()
            if not files:
                return False
            if files == self.NOTEXIST:
                return self.NOTEXIST
            self._rootFile = files
            return True
        except:
            return False

    def indexDrive(self,guiObj=None):
        if self.isConnected():
            self.fileCount = 0
            letter = DriveUtil.getLetter(self._name,self._serial)
            if self.getValidExt():
                validext = '|'.join(self.getValidExt())
                validext = "\.(" + validext + ")$"
            else:
                validext = None
            skip = '|'.join(self.getSkipList())
            self._rootFile = self.index("",letter,validext,skip,root=True,guiObj=guiObj)
            self.updateFreeSpace()
            return True
        else:
            return drive.MISSING
        return False

    def getFileList(self):
        return self.getRootFile().getFileList()

    def index(self,path,letter,validext,skipIn,root=False,guiObj=None):
        dir = os.path.join(letter+":"+os.sep,path)
        if not os.path.isdir(dir):
            print "Can only call index on a directory!\n"
            return None
        try:
            files = os.listdir(dir)
        except:
            return None
        dirObj = file(os.path.basename(dir),
                      os.path.dirname(path),
                      self._name+":",
                      True,
                      0,
                      0,
                      self._name,self._serial)
        if skipIn:
            skip = re.compile(str(skipIn),re.IGNORECASE)
        else:
            skip = None
        passedFiles = []
        if validext:
            #print validext
            ext = re.compile(validext,re.IGNORECASE)
        else:
            ext = None
        for foundFile in files[:]:
            if not os.path.isdir(os.path.join(dir,foundFile)):
                if skip:
                    if re.search(skip,foundFile):
                        continue
                if ext:
                    if not re.search(ext,foundFile):
                        continue
                self.fileCount += 1
                if guiObj:
                    guiObj.SetStatusText("Refreshing " + self.getName() + "-" + self.getSerial() + ' - Found %i file(s)... %s'%(self.fileCount,foundFile),1)
            newFile = None
            if os.path.isdir(os.path.join(dir,foundFile)):
                newFile = self.index(os.path.join(path,foundFile),letter,validext,skip,guiObj=guiObj)
            else:
                newFile = file(foundFile,path,self._name+":",False,float(os.path.getsize(os.path.join(dir,foundFile)))/float(1024),int(os.path.getmtime(os.path.join(dir,foundFile))),self._name,self._serial)
            if newFile:
                newFile.setParent(dirObj)
                passedFiles.append(newFile)
        if not passedFiles and not root:
            return None
        dirObj.setFiles(passedFiles)
        #print dirObj.getName()
        return dirObj

    def isConnected(self):
        if self._name is not None and self._serial is not None:
            letter = DriveUtil.getLetter(self._name,self._serial)
            if letter is not None:
                return True
        return False

    def findDir(self,dir):
        if self.getRootFile().findDir(dir):
            return True
        else:
            for pendingDir in self._pendingWriteDirs:
                if pendingDir == dir:
                    return True
        return False

    def updateFreeSpace(self):
        letter = DriveUtil.getLetter(self._name,self._serial)
        if not letter:
            print "Cannot update freespace for " + self._name + " when drive is not connected!\n"
            return False
        freeSpace = DriveUtil.getFreeSpace(letter)
        self.setFreeSpace(freeSpace)
        return True

